package com.yuan.java.nettydemo.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.junit.Test;

import java.nio.charset.Charset;

/**
 * @author yuaneg 2020/1/15
 */
public class WebSocketServer {

    @Test
    public void run() throws InterruptedException {

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        private WebSocketServerHandshaker handshaker;

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new HttpServerCodec());
                            ch.pipeline().addLast(new HttpObjectAggregator(78787));
                            ch.pipeline().addLast(new ChunkedWriteHandler());
                            ch.pipeline().addLast(new SimpleChannelInboundHandler<Object>() {
                                @Override
                                protected void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception {
                                    if (msg instanceof FullHttpRequest) {
                                        handleHttpRequest(ctx, (FullHttpRequest) msg);

                                    }
                                    if (msg instanceof WebSocketFrame) {
                                        handleWebSocketFrame(ctx, (WebSocketFrame) msg);
                                    }
                                }
                            });
                        }

                        @Override
                        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                            ctx.flush();
                        }

                        private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
                            if (!request.getDecoderResult().isSuccess() || !("websocket".equals(request.headers().get("Upgrade")))) {
                                sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
                            }
                            WebSocketServerHandshakerFactory webSocketServerHandshakerFactory = new WebSocketServerHandshakerFactory("ws://127.0.0.1:8080/websocket", null, false);

                            handshaker = webSocketServerHandshakerFactory.newHandshaker(request);
                            if (handshaker == null) {
                                WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
                            } else {
                                handshaker.handshake(ctx.channel(), request);
                            }
                        }

                        private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
                            if (frame instanceof CloseWebSocketFrame) {
                                handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain());
                                return;
                            }
                            String request = ((TextWebSocketFrame) frame).text();
                            System.out.println(request);
                            ctx.channel().writeAndFlush(new TextWebSocketFrame("helllsdfsdfsdfsdf"));
                        }

                        private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
                            if (!response.getStatus().equals(HttpResponseStatus.OK)) {
                                ByteBuf byteBuf = Unpooled.copiedBuffer(response.getStatus().toString(), Charset.forName("UTF-8"));
                                response.content().writeBytes(byteBuf);
                                byteBuf.release();
                            }
                        }
                    });
            serverBootstrap.bind(8080).sync().channel().closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }


}
